const path = require('path')
const ejs = require('ejs')
const marked = require('marked')
const frontMatter = require('front-matter')
const glob = require('glob')
const fs = require('fs')
const dayjs = require('dayjs')
const hljs = require('highlight.js')

dayjs.extend(require('dayjs/plugin/advancedFormat.js'))
dayjs.extend(require('dayjs/plugin/customParseFormat.js'))

const feed = require('./feed.js')
const utils = require('./utils.js')

var pageDict = {}

marked.setOptions(
    {
        renderer: new marked.Renderer(),
        highlight: function(code, lang) {
            const language = hljs.getLanguage(lang) ? lang : "plaintext"
            return hljs.highlight(code, {language}).value
        },
        langPrefix: 'hljs language-',
        breaks: true
    }
)

var config = {
    title: "CONF: Blog Builder Fucker Fucker website",
    description: "CONF: a static blog website made by Blog Builder",
    author: "CONF: fucker fucker",
    theme: "tiny",// config it
    dateFormat: "YYYY-MM-DD",
    adminTheme: "overlord",
    icon: "",
    maxComments: 500,
    maxCommentsLength: 2500,
    emailNotifications: false,
    generatorVersion: ""
}

const dirs = {
    // prefix with gen_ is auto generate folder, rm -rf them will be ok
    src: process.env['BB_SRC_PATH'] || 'gen_source',
    out: process.env['BB_OUTPUT_PATH'] || 'gen_public',
    data: process.env['BB_DATA_PATH'] || 'gen_data',
    themes: process.env['BB_THEMES_PATH'] || 'themes',
    keep: process.env['BB_KEEP_LIST']
}

// config your own ejs file
const loadLayout = (layout) => {
    const file = `${dirs.themes}/tiny/layouts/${layout}.ejs`;
    const data = fs.readFileSync(file, 'utf-8');

    return {file, data}
}


function buildIndex() {
    const layoutName = "index"
    const layout  = loadLayout(layoutName)

    const completePage = ejs.render(
        layout.data,
        {
            filename: `${dirs.themes}/${config.theme}/layouts/${layoutName}`,
            pages: getPages(),
            config
        }
    )

    fs.writeFileSync(`${dirs.out}/index.html`, completePage)

    feed.build()
}

function buildPage(file) {
    const filepath = path.parse(file)
    let destPath = path.join(dirs.out, filepath.dir)

    if (filepath.name == 'index')
        return

    if (filepath.name !== 'index')
        destPath = path.join(destPath, filepath.name)

    const fullFilename = `${dirs.src}/pages/${file}`

    if (!fs.existsSync(fullFilename)) {
        console.log(`[ERROR]: File does not exist: ${fullFilename}`)
        return
    }
    // frontMatter read file
    const pageData = frontMatter(fs.readFileSync(fullFilename, 'utf-8'))
    const templateConfig = {
        page: pageData.attributes
    }

    let pageContent

    switch (filepath.ext) {
    case '.md':
        pageContent = marked.parse(pageData.body) // convert md to html page
        break
    case '.ejs':
        pageContent = ejs.render(pageData.body, templateConfig) // render ejs syntax
        break
    default:
        pageContent = pageData.body // default leave it plain text
    }

    const layoutName = pageData.attributes.layout || 'default'
    const layout = loadLayout(layoutName)

    // collect comments as json file
    var commentsFile = `${dirs.data}/${filepath.name}/comments.json`
    var comments = []
    if (fs.existsSync(commentsFile)) {
        comments = JSON.parse(fs.readFileSync(commentsFile), 'utf-8')

        if (config.commentsRequireApproval)
            comments = comments.filter(c => c.approved)
    }

    var pageDate = pageData.attributes.date
    if (pageDate) {
        if (typeof pageDate.getMonth === 'function') {
            // default setting
            pageData.attributes.date = dayjs(pageDate).format(config.dateFormat)
        }
        else {
            // load config.json setting
            pageData.attributes.date = dayjs(pageDate, config.dateFormat).format(config.dateFormat)
        }
    }

    var utctime = new Date(pageData.attributes.date).getTime()
    if (!utctime) utctime = 0

    pageDict[filepath.name] = Object.assign(
        {
            utctime,
            url: filepath.name,
        },
        pageData.attributes
    )

    if (pageData.attributes.draft)
        return pageDict[filepath.name]

    const completePage = ejs.render(
        layout.data,
        Object.assign({}, templateConfig, {
            body: pageContent,
            filename: `${dirs.themes}/${config.theme}/layouts/${layoutName}`,
            config,
            comments
        })
    )

    fs.mkdirSync(destPath, {
        recursive: true
    })
    fs.writeFileSync(`${destPath}/index.html`, completePage); // rewrite index.html, it sucks

    return pageDict[filepath.name]
}



function getPages() {
    let pg = []

    for (const key in pageDict) {
        pg.push(Object.assign({name: key}, pageDict[key]))
    }

    return pg.sort((a, b) => {
        a.utctime == b.utctime ? 0 : b.utctime - a.utctime
    })
}

// auto gen dir
function createDirs() {
    if (!fs.existsSync(dirs.src))
        fs.mkdirSync(dirs.src)
    if (!fs.existsSync(dirs.data))
        fs.mkdirSync(dirs.data)
    if (!fs.existsSync(`${dirs.src}/pages`))
        fs.mkdirSync(`${dirs.src}/pages`)
    if (!fs.existsSync(`${dirs.src}/media`))
        fs.mkdirSync(`${dirs.src}/media`)

    if (!fs.existsSync(`${dirs.data}/config.json`))
        fs.writeFileSync(`${dirs.data}/config.json`, JSON.stringify(config))

    feed.init()
}

// copy .. folder to current folder ?
function makeApp() {
    utils.copyFolderSync(`${__dirname}/..`, ".")

    createDirs()

    console.log('[INFO]: auto gen dir ok, site initialized')
}

// get config
function getConfig() {
    return config
}

function setConfig(obj) {
    let status = "[INFO]: config setting updated"
    
    config.title = obj.title
    config.description = obj.description
    config.author = obj.author
    config.dateFormat = obj.dateFormat
    // what the fuck is this field?
    config.commentsRequireApproval = !!obj.commentsRequireApproval

    if (obj.theme && fs.existsSync(`${dirs.themes}/${obj.theme}`))
        config.theme = obj.theme
    else status = "[ERROR]: invalid theme"

    fs.writeFileSync(`${dirs.data}/config.json`, JSON.stringify(config))

    return status
} 

function build() {
    pageDict = {}

    createDirs()

    config = Object.assign(config, JSON.parse(fs.readFileSync(`${dirs.data}/config.json`)))
    config.generatorVersion = utils.getVersion()

    // theme
    if (fs.existsSync(`${dirs.themes}/${config.theme}/assets`)) {
        utils.copyFolderSync(`themes/${config.theme}/assets`, dirs.out)
    }

    // admin theme
    if (fs.existsSync(`themes/${config.adminTheme}/assets`)) {
        utils.copyFolderSync(`themes/${config.adminTheme}/assets`, dirs.out);
    }

    if (fs.existsSync(`${dirs.src}/media`)) {
        utils.copyFolderSync(`${dirs.src}/media`, `${dirs.out}/media`);
    }

    if (fs.existsSync(`${dirs.src}/upload`)) {
        utils.copyFolderSync(`${dirs.src}/upload`, dirs.out);
    }
    // .md .ejs .html to be builded as html file
    const files = glob.sync('**/*.@(md|ejs|html)', { cwd: `${dirs.src}/pages` });
    files.forEach(file => buildPage(file));

    buildIndex();
  
    console.log('[INFO]: Site built succesfully');
}

module.exports = { 
    build, 
    buildPage, 
    buildIndex, 
    dirs, 
    makeApp, 
    getConfig, 
    setConfig, 
    getPages 
}